From 6f46072ed02afb1fa6c9b2d72231b27e9b776958 Mon Sep 17 00:00:00 2001 From: "kaf24@freefall.cl.cam.ac.uk" Date: Fri, 22 Oct 2004 15:51:39 +0000 Subject: [PATCH] bitkeeper revision 1.1159.130.1 (41792c8bfIrnaq8cezNM7nbYpseVFQ) Extend Xen's evtchn interface. --- tools/libxc/xc.h | 3 + tools/libxc/xc_evtchn.c | 29 +++++- tools/python/xen/lowlevel/xc/xc.c | 37 ++++++- tools/python/xen/lowlevel/xu/xu.c | 2 +- xen/common/event_channel.c | 108 ++++++++++++++++++--- xen/include/hypervisor-ifs/event_channel.h | 47 +++++++-- 6 files changed, 198 insertions(+), 28 deletions(-) diff --git a/tools/libxc/xc.h b/tools/libxc/xc.h index ddee505e5f..027d76711f 100644 --- a/tools/libxc/xc.h +++ b/tools/libxc/xc.h @@ -146,6 +146,9 @@ typedef struct { } u; } xc_evtchn_status_t; +int xc_evtchn_alloc_unbound(int xc_handle, + u32 dom, + int *port); int xc_evtchn_bind_interdomain(int xc_handle, u32 dom1, /* may be DOMID_SELF */ u32 dom2, /* may be DOMID_SELF */ diff --git a/tools/libxc/xc_evtchn.c b/tools/libxc/xc_evtchn.c index 52f7377467..2f78ef7936 100644 --- a/tools/libxc/xc_evtchn.c +++ b/tools/libxc/xc_evtchn.c @@ -31,6 +31,26 @@ static int do_evtchn_op(int xc_handle, evtchn_op_t *op) } +int xc_evtchn_alloc_unbound(int xc_handle, + u32 dom, + int *port) +{ + evtchn_op_t op; + int rc; + + op.cmd = EVTCHNOP_alloc_unbound; + op.u.alloc_unbound.dom = (domid_t)dom; + + if ( (rc = do_evtchn_op(xc_handle, &op)) == 0 ) + { + if ( port != NULL ) + *port = op.u.alloc_unbound.port; + } + + return rc; +} + + int xc_evtchn_bind_interdomain(int xc_handle, u32 dom1, u32 dom2, @@ -41,9 +61,12 @@ int xc_evtchn_bind_interdomain(int xc_handle, int rc; op.cmd = EVTCHNOP_bind_interdomain; - op.u.bind_interdomain.dom1 = (domid_t)dom1; - op.u.bind_interdomain.dom2 = (domid_t)dom2; - + op.u.bind_interdomain.dom1 = (domid_t)dom1; + op.u.bind_interdomain.dom2 = (domid_t)dom2; + op.u.bind_interdomain.port1 = (port1 != NULL) ? *port1 : 0; + op.u.bind_interdomain.port2 = (port2 != NULL) ? *port2 : 0; + + if ( (rc = do_evtchn_op(xc_handle, &op)) == 0 ) { if ( port1 != NULL ) diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 61287b2c38..2dd8680b4f 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -462,6 +462,26 @@ static PyObject *pyxc_bvtsched_domain_get(PyObject *self, "warpu", warpu); } +static PyObject *pyxc_evtchn_alloc_unbound(PyObject *self, + PyObject *args, + PyObject *kwds) +{ + XcObject *xc = (XcObject *)self; + + u32 dom; + int port; + + static char *kwd_list[] = { "dom", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) ) + return NULL; + + if ( xc_evtchn_alloc_unbound(xc->xc_handle, dom, &port) != 0 ) + return PyErr_SetFromErrno(xc_error); + + return PyInt_FromLong(port); +} + static PyObject *pyxc_evtchn_bind_interdomain(PyObject *self, PyObject *args, PyObject *kwds) @@ -469,12 +489,12 @@ static PyObject *pyxc_evtchn_bind_interdomain(PyObject *self, XcObject *xc = (XcObject *)self; u32 dom1 = DOMID_SELF, dom2 = DOMID_SELF; - int port1, port2; + int port1 = 0, port2 = 0; - static char *kwd_list[] = { "dom1", "dom2", NULL }; + static char *kwd_list[] = { "dom1", "dom2", "port1", "port2", NULL }; - if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list, - &dom1, &dom2) ) + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiii", kwd_list, + &dom1, &dom2, &port1, &port2) ) return NULL; if ( xc_evtchn_bind_interdomain(xc->xc_handle, dom1, @@ -965,6 +985,13 @@ static PyMethodDef pyxc_methods[] = { "Returns [dict]:\n" " slice [long]: Scheduler time slice.\n" }, + { "evtchn_alloc_unbound", + (PyCFunction)pyxc_evtchn_alloc_unbound, + METH_VARARGS | METH_KEYWORDS, "\n" + "Allocate an unbound local port that will await a remote connection.\n" + " dom [int]: Remote domain to accept connections from.\n\n" + "Returns: [int] Unbound event-channel port.\n" }, + { "evtchn_bind_interdomain", (PyCFunction)pyxc_evtchn_bind_interdomain, METH_VARARGS | METH_KEYWORDS, "\n" @@ -985,7 +1012,7 @@ static PyMethodDef pyxc_methods[] = { { "evtchn_close", (PyCFunction)pyxc_evtchn_close, METH_VARARGS | METH_KEYWORDS, "\n" - "Close an event channel.\n" + "Close an event channel. If interdomain, sets remote end to 'unbound'.\n" " dom [int, SELF]: Dom-id of one endpoint of the channel.\n" " port [int]: Port-id of one endpoint of the channel.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, diff --git a/tools/python/xen/lowlevel/xu/xu.c b/tools/python/xen/lowlevel/xu/xu.c index db3aa1d5d4..4af52375fd 100644 --- a/tools/python/xen/lowlevel/xu/xu.c +++ b/tools/python/xen/lowlevel/xu/xu.c @@ -1059,7 +1059,7 @@ static PyObject *xu_port_new(PyObject *self, PyObject *args) { xu_port_object *xup; u32 dom; - int port1, port2; + int port1 = 0, port2 = 0; if ( !PyArg_ParseTuple(args, "i", &dom) ) return NULL; diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index 5a0e1fbf25..3ac51e53b6 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -29,6 +29,7 @@ #define INIT_EVENT_CHANNELS 16 #define MAX_EVENT_CHANNELS 1024 + static int get_free_port(struct domain *d) { int max, port; @@ -67,16 +68,44 @@ static int get_free_port(struct domain *d) return port; } + +static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) +{ + struct domain *d = current; + int port; + + spin_lock(&d->event_channel_lock); + + if ( (port = get_free_port(d)) >= 0 ) + { + d->event_channel[port].state = ECS_UNBOUND; + d->event_channel[port].u.unbound.remote_domid = alloc->dom; + } + + spin_unlock(&d->event_channel_lock); + + if ( port < 0 ) + return port; + + alloc->port = port; + return 0; +} + + static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) { +#define ERROR_EXIT(_errno) do { rc = (_errno); goto out; } while ( 0 ) struct domain *d1, *d2; - int port1 = 0, port2 = 0; + int port1 = bind->port1, port2 = bind->port2; domid_t dom1 = bind->dom1, dom2 = bind->dom2; long rc = 0; - if ( !IS_PRIV(current) ) + if ( !IS_PRIV(current) && (dom1 != DOMID_SELF) ) return -EPERM; + if ( (port1 < 0) || (port2 < 0) ) + return -EINVAL; + if ( dom1 == DOMID_SELF ) dom1 = current->id; if ( dom2 == DOMID_SELF ) @@ -103,25 +132,73 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) spin_lock(&d1->event_channel_lock); } - if ( (port1 = get_free_port(d1)) < 0 ) + /* Obtain, or ensure that we already have, a valid . */ + if ( port1 == 0 ) { - rc = port1; - goto out; + if ( (port1 = get_free_port(d1)) < 0 ) + ERROR_EXIT(port1); } + else if ( port1 >= d1->max_event_channel ) + ERROR_EXIT(-EINVAL); - /* 'Allocate' port1 before searching for a free port2. */ - d1->event_channel[port1].state = ECS_INTERDOMAIN; + /* Obtain, or ensure that we already have, a valid . */ + if ( port2 == 0 ) + { + /* Make port1 non-free while we allocate port2 (in case dom1==dom2). */ + u16 tmp = d1->event_channel[port1].state; + d1->event_channel[port1].state = ECS_INTERDOMAIN; + port2 = get_free_port(d2); + d1->event_channel[port1].state = tmp; + if ( port2 < 0 ) + ERROR_EXIT(port2); + } + else if ( port2 >= d2->max_event_channel ) + ERROR_EXIT(-EINVAL); - if ( (port2 = get_free_port(d2)) < 0 ) + /* Validate 's current state. */ + switch ( d1->event_channel[port1].state ) { - d1->event_channel[port1].state = ECS_FREE; - rc = port2; + case ECS_FREE: + break; + + case ECS_UNBOUND: + if ( d1->event_channel[port1].u.unbound.remote_domid != dom2 ) + ERROR_EXIT(-EINVAL); + break; + + case ECS_INTERDOMAIN: + rc = ((d1->event_channel[port1].u.interdomain.remote_dom != d2) || + (d1->event_channel[port1].u.interdomain.remote_port != port2)) ? + -EINVAL : 0; goto out; + + default: + ERROR_EXIT(-EINVAL); + } + + /* Validate 's current state. */ + switch ( d2->event_channel[port2].state ) + { + case ECS_FREE: + break; + + case ECS_UNBOUND: + if ( d2->event_channel[port2].u.unbound.remote_domid != dom1 ) + ERROR_EXIT(-EINVAL); + break; + + default: + ERROR_EXIT(-EINVAL); } + /* + * Everything checked out okay -- bind to . + */ + d1->event_channel[port1].u.interdomain.remote_dom = d2; d1->event_channel[port1].u.interdomain.remote_port = (u16)port2; - + d1->event_channel[port1].state = ECS_INTERDOMAIN; + d2->event_channel[port2].u.interdomain.remote_dom = d1; d2->event_channel[port2].u.interdomain.remote_port = (u16)port1; d2->event_channel[port2].state = ECS_INTERDOMAIN; @@ -138,6 +215,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) bind->port2 = port2; return rc; +#undef ERROR_EXIT } @@ -295,6 +373,7 @@ static long __evtchn_close(struct domain *d1, int port1) BUG(); chn2[port2].state = ECS_UNBOUND; + chn2[port2].u.unbound.remote_domid = d1->id; break; default: @@ -397,6 +476,7 @@ static long evtchn_status(evtchn_status_t *status) break; case ECS_UNBOUND: status->status = EVTCHNSTAT_unbound; + status->u.unbound.dom = chn[port].u.unbound.remote_domid; break; case ECS_INTERDOMAIN: status->status = EVTCHNSTAT_interdomain; @@ -432,6 +512,12 @@ long do_event_channel_op(evtchn_op_t *uop) switch ( op.cmd ) { + case EVTCHNOP_alloc_unbound: + rc = evtchn_alloc_unbound(&op.u.alloc_unbound); + if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) ) + rc = -EFAULT; /* Cleaning up here would be a mess! */ + break; + case EVTCHNOP_bind_interdomain: rc = evtchn_bind_interdomain(&op.u.bind_interdomain); if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) ) diff --git a/xen/include/hypervisor-ifs/event_channel.h b/xen/include/hypervisor-ifs/event_channel.h index fd6d0325f8..2da4f06d24 100644 --- a/xen/include/hypervisor-ifs/event_channel.h +++ b/xen/include/hypervisor-ifs/event_channel.h @@ -10,17 +10,43 @@ #define __HYPERVISOR_IFS__EVENT_CHANNEL_H__ /* - * EVTCHNOP_bind_interdomain: Open an event channel between and . + * EVTCHNOP_alloc_unbound: Allocate a fresh local port and prepare + * it for binding to . + */ +#define EVTCHNOP_alloc_unbound 6 +typedef struct { + /* IN parameters */ + domid_t dom; /* 0 */ + u16 __pad; + /* OUT parameters */ + u32 port; /* 4 */ +} PACKED evtchn_alloc_unbound_t; /* 8 bytes */ + +/* + * EVTCHNOP_bind_interdomain: Construct an interdomain event channel between + * and . Either or may be wildcarded by setting to + * zero. On successful return both and are filled in and + * is fully bound to . + * * NOTES: - * 1. and/or may be specified as DOMID_SELF. - * 2. Only a sufficiently-privileged domain may create an event channel. - * 3. and are only supplied if the op succeeds. + * 1. A wildcarded port is allocated from the relevant domain's free list + * (i.e., some port that was previously EVTCHNSTAT_closed). However, if the + * remote port pair is already fully bound then a port is not allocated, + * and instead the existing local port is returned to the caller. + * 2. If the caller is unprivileged then must be DOMID_SELF. + * 3. If the caller is unprivileged and is EVTCHNSTAT_closed + * then must be DOMID_SELF. + * 4. If either port is already bound then it must be bound to the other + * specified domain and port (if not wildcarded). + * 5. If either port is awaiting binding (EVTCHNSTAT_unbound) then it must + * be awaiting binding to the other domain, and the other port pair must + * be closed or unbound. */ #define EVTCHNOP_bind_interdomain 0 typedef struct { /* IN parameters. */ domid_t dom1, dom2; /* 0, 2 */ - /* OUT parameters. */ + /* IN/OUT parameters. */ u32 port1, port2; /* 4, 8 */ } PACKED evtchn_bind_interdomain_t; /* 12 bytes */ @@ -55,7 +81,8 @@ typedef struct { /* * EVTCHNOP_close: Close the communication channel which has an endpoint at - * . + * . If the channel is interdomain then the remote end is placed in + * the unbound state (EVTCHNSTAT_unbound), awaiting a new connection. * NOTES: * 1. may be specified as DOMID_SELF. * 2. Only a sufficiently-privileged domain may close an event channel @@ -96,13 +123,16 @@ typedef struct { u16 __pad; u32 port; /* 4 */ /* OUT parameters */ -#define EVTCHNSTAT_closed 0 /* Chennel is not in use. */ -#define EVTCHNSTAT_unbound 1 /* Channel is not bound to a source. */ +#define EVTCHNSTAT_closed 0 /* Channel is not in use. */ +#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/ #define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */ #define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */ #define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */ u32 status; /* 8 */ union { /* 12 */ + struct { + domid_t dom; /* 12 */ + } PACKED unbound; /* EVTCHNSTAT_unbound */ struct { domid_t dom; /* 12 */ u16 __pad; @@ -117,6 +147,7 @@ typedef struct { u32 cmd; /* EVTCHNOP_* */ /* 0 */ u32 __reserved; /* 4 */ union { /* 8 */ + evtchn_alloc_unbound_t alloc_unbound; evtchn_bind_interdomain_t bind_interdomain; evtchn_bind_virq_t bind_virq; evtchn_bind_pirq_t bind_pirq; -- 2.30.2